#ifndef GML__ITERATOR_H
#define GML__ITERATOR_H

#include <stddef.h>

#include "config.h"

#define advance(_T, _iterator, _n) ({ \
    _T##Algo(advance)(_T, _iterator, _n); \
})

#define advanceInput(_T, _iterator, _n) ({ \
    defineArg(_iterator, __iterator); \
    defineArg(_n, __n); \
    for (; __n > 0; --__n) { \
        _T##Func(IteratorIncreasePrefix)(&__iterator); \
    } \
    __iterator; \
})

#define advanceForward(_T, _iterator, _n) ({ \
    advanceInput(_T, _iterator, _n); \
})

#define advanceBidirectional(_T, _iterator, _n) ({ \
    defineArg(_iterator, __iterator); \
    defineArg(_n, __n); \
    if (__n >= 0) { \
        for (; __n > 0; --__n) { \
            _T##Func(IteratorIncreasePrefix)(&__iterator); \
        } \
    } \
    else { \
        for (; __n < 0; ++__n) { \
            _T##Func(IteratorDecreasePrefix)(&__iterator); \
        } \
    } \
    __iterator; \
})

#define advanceRandomAccess(_T, _iterator, _n) ({ \
    defineArg(_iterator, __iterator); \
    defineArg(_n, __n); \
    __iterator = _T##Func(IteratorAdd)(&__iterator, __n); \
    __iterator; \
})

#define center(_T, _first, _last) ({ \
    _T##Algo(center)(_T, _first, _last); \
})

#define centerInput(_T, _first, _last) ({ \
    defineArg(_first, __first); \
    defineArg(_last, __last); \
    __typeof__(__first) __fast = __first; \
    __typeof__(__first) __slow = __first; \
    while (_T##Func(IteratorNotEqualTo)(&__fast, &__last) && _T##Func(IteratorNotEqualTo)(_T##Func(IteratorIncreasePrefix)(&__fast), &__last)) { \
        _T##Func(IteratorIncreasePrefix)(&__slow); \
        _T##Func(IteratorIncreasePrefix)(&__fast); \
    } \
    __slow; \
})

#define centerForward(_T, _first, _last) ({ \
    centerInput(_T, _first, _last); \
})

#define centerBidirectional(_T, _first, _last) ({ \
    centerForward(_T, _first, _last); \
})

#define centerRandomAccess(_T, _first, _last) ({ \
    defineArg(_first, __first); \
    defineArg(_last, __last); \
    ptrdiff_t __distance = distanceRandomAccess(_T, __first, __last); \
    __first = _T##Func(IteratorAdd)(&__first, __distance >> 1); \
    __first; \
})

#define distance(_T, _first, _last) ({ \
    _T##Algo(distance)(_T, _first, _last); \
})

#define distanceInput(_T, _first, _last) ({ \
    defineArg(_first, __first); \
    defineArg(_last, __last); \
    size_t __distance = 0; \
    for (; _T##Func(IteratorNotEqualTo)(&__first, &__last); _T##Func(IteratorIncreasePrefix)(&__first)) { \
        ++__distance; \
    } \
    __distance; \
})

#define distanceForward(_T, _first, _last) ({ \
    distanceInput(_T, _first, _last); \
})

#define distanceBidirectional(_T, _first, _last) ({ \
    distanceForward(_T, _first, _last); \
})

#define distanceRandomAccess(_T, _first, _last) ({ \
    defineArg(_first, __first); \
    defineArg(_last, __last); \
    ptrdiff_t __distance = _T##Func(IteratorDiff)(&__last, &__first); \
    __distance; \
})

#endif // GML__ITERATOR_H
